package org.gpp.proj1.communication.protocol;

import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import junit.framework.TestCase;

import org.gpp.proj1.logic.core.board.FacingSides;
import org.gpp.proj1.logic.core.board.RobotPosition;
import org.gpp.proj1.logic.metadata.PartEntry;
import org.gpp.proj1.logic.metadata.RobotEntry;
import org.gpp.proj1.logic.metadata.RobotPartCategory;
import org.gpp.proj1.robot.part.Part;

/**
 * @author Paolo Victor, paolovictor@gmail.com
 */
public class ProtocolMessageParserTest extends TestCase {

	private static final String ALIAS1 = "testAlias1";
	private static final String ALIAS2 = "testAlias2";
	private static final String ALIAS3 = "testAlias3";
	private static final String CLIENT_ID1 = "clientId1";
	private static final String CLIENT_ID2 = "clientId2";
	private static final String CLIENT_ID3 = "clientId3";
	
	private static final String TEST_ROBOT_1 = "robot1";
	private static final String TEST_ROBOT_2 = "robot2";
	private static final String TEST_ROBOT_OWNER = "robotOwner";
	private static final String TEST_ROBOT_OWNER_ID = "123123";

	
	public void testParseAliasMap() {
		// Note that both map and set are ordered! That's necessary
		// to ensure the order!
		Map<String, String> aliasMap = new TreeMap<String, String>();		
		Set<String> readyClients = new TreeSet<String>();
		
		aliasMap.put( CLIENT_ID1, ALIAS1 );
		aliasMap.put( CLIENT_ID2, ALIAS2 );
		aliasMap.put( CLIENT_ID3, ALIAS3 );
		
		readyClients.add( CLIENT_ID1 );
		readyClients.add( CLIENT_ID3 );
		
		Message message = ProtocolMessageFactory.buildWhosHereMessage(aliasMap, readyClients);
		
		assertEquals( ProtocolIdentifiers.WHOSHERE, message.getIdentifier() );

		assertEquals( "3", message.getParameters()[0] );
		assertEquals( CLIENT_ID1, message.getParameters()[1] ); // See? That's why we need to ensure order!
		assertEquals( ALIAS1, message.getParameters()[2] );
		assertEquals( CLIENT_ID2, message.getParameters()[3] );
		assertEquals( ALIAS2, message.getParameters()[4] );
		assertEquals( CLIENT_ID3, message.getParameters()[5] );
		assertEquals( ALIAS3, message.getParameters()[6] );
		
		assertEquals( "2", message.getParameters()[7] );
		assertEquals( CLIENT_ID1, message.getParameters()[8] );
		assertEquals( CLIENT_ID3, message.getParameters()[9] );
		
		// Parsing the message
		Map<String, String> parsedAliasMap = ProtocolMessageParser.parseAliasMap( message );
		
		assertEquals( aliasMap, parsedAliasMap );
	}

	public void testParseReadySet() {
		// Note that both map and set are ordered! That's necessary
		// to ensure the order!
		Map<String, String> aliasMap = new TreeMap<String, String>();		
		Set<String> readyClients = new TreeSet<String>();
		
		aliasMap.put( CLIENT_ID1, ALIAS1 );
		aliasMap.put( CLIENT_ID2, ALIAS2 );
		aliasMap.put( CLIENT_ID3, ALIAS3 );
		
		readyClients.add( CLIENT_ID1 );
		readyClients.add( CLIENT_ID3 );
		
		Message message = ProtocolMessageFactory.buildWhosHereMessage(aliasMap, readyClients);
		
		assertEquals( ProtocolIdentifiers.WHOSHERE, message.getIdentifier() );

		assertEquals( "3", message.getParameters()[0] );
		assertEquals( CLIENT_ID1, message.getParameters()[1] ); // See? That's why we need to ensure order!
		assertEquals( ALIAS1, message.getParameters()[2] );
		assertEquals( CLIENT_ID2, message.getParameters()[3] );
		assertEquals( ALIAS2, message.getParameters()[4] );
		assertEquals( CLIENT_ID3, message.getParameters()[5] );
		assertEquals( ALIAS3, message.getParameters()[6] );
		
		assertEquals( "2", message.getParameters()[7] );
		assertEquals( CLIENT_ID1, message.getParameters()[8] );
		assertEquals( CLIENT_ID3, message.getParameters()[9] );
		
		// Parsing the message
		Set<String> parsedReadySet = ProtocolMessageParser.parseReadySet( message );
		
		assertEquals( readyClients, parsedReadySet );
	}
	
	public void testParseMyRobotData() {

		List<RobotEntry> robots = createMockRobotData();
		
		// Phew! Now to test the message building =)
		Message message = ProtocolMessageFactory.buildMyRobotListMessage( ALIAS1, robots );
		
		// Parsing the message
		String[] robotData = ProtocolMessageParser.parseMyRobotData( message );
		
		assertEquals( TEST_ROBOT_1, robotData[ 1 ] );
		assertEquals( "Head1", robotData[ 2 ] );
		assertEquals( "Torso1", robotData[ 3 ] );
		assertEquals( "ArmLeft1", robotData[ 4 ] );
		assertEquals( "ArmRight1", robotData[ 5 ] );
		assertEquals( "Legs1", robotData[ 6 ] );
		
		assertEquals( TEST_ROBOT_2, robotData[ 7 ] );
		assertEquals( "Head2", robotData[ 8 ] );
		assertEquals( "Torso2", robotData[ 9 ] );
		assertEquals( "ArmLeft2", robotData[ 10 ] );
		assertEquals( "ArmRight2", robotData[ 11 ] );
		assertEquals( "Legs2", robotData[ 12 ] );
	}
	
	public void testParsePlayerRobotData() {
		
		List<RobotEntry> robots = createMockRobotData();
		
		// Building my robot data message
		Message message = ProtocolMessageFactory.buildMyRobotListMessage( ALIAS1, robots );
		
		// Parsing it to a robot data array
		String[] robotData = ProtocolMessageParser.parseMyRobotData( message );
		
		assertEquals( TEST_ROBOT_1, robotData[ 1 ] );
		assertEquals( "Head1", robotData[ 2 ] );
		assertEquals( "Torso1", robotData[ 3 ] );
		assertEquals( "ArmLeft1", robotData[ 4 ] );
		assertEquals( "ArmRight1", robotData[ 5 ] );
		assertEquals( "Legs1", robotData[ 6 ] );
		
		assertEquals( TEST_ROBOT_2, robotData[ 7 ] );
		assertEquals( "Head2", robotData[ 8 ] );
		assertEquals( "Torso2", robotData[ 9 ] );
		assertEquals( "ArmLeft2", robotData[ 10 ] );
		assertEquals( "ArmRight2", robotData[ 11 ] );
		assertEquals( "Legs2", robotData[ 12 ] );
		
		// Now, creating a Player Robot Data message
		message = ProtocolMessageFactory.buildPlayerRobotDataMessage( TEST_ROBOT_OWNER_ID, robotData );
		
		// Parsing the message
		Map<String, Map<RobotPartCategory, String>> mapping = ProtocolMessageParser.parseRobotData( message );
		
		assertTrue( mapping.containsKey( TEST_ROBOT_1 ) );
		assertEquals( "Head1", mapping.get( TEST_ROBOT_1 ).get( RobotPartCategory.HEAD ) );
		assertEquals( "Torso1", mapping.get( TEST_ROBOT_1 ).get( RobotPartCategory.TORSO ) );
		assertEquals( "ArmLeft1", mapping.get( TEST_ROBOT_1 ).get( RobotPartCategory.ARM_LEFT ) );
		assertEquals( "ArmRight1", mapping.get( TEST_ROBOT_1 ).get( RobotPartCategory.ARM_RIGHT ) );
		assertEquals( "Legs1", mapping.get( TEST_ROBOT_1 ).get( RobotPartCategory.LEGS ) );
		
		assertTrue( mapping.containsKey( TEST_ROBOT_2 ) );
		assertEquals( "Head2", mapping.get( TEST_ROBOT_2 ).get( RobotPartCategory.HEAD ) );
		assertEquals( "Torso2", mapping.get( TEST_ROBOT_2 ).get( RobotPartCategory.TORSO ) );
		assertEquals( "ArmLeft2", mapping.get( TEST_ROBOT_2 ).get( RobotPartCategory.ARM_LEFT ) );
		assertEquals( "ArmRight2", mapping.get( TEST_ROBOT_2 ).get( RobotPartCategory.ARM_RIGHT ) );
		assertEquals( "Legs2", mapping.get( TEST_ROBOT_2 ).get( RobotPartCategory.LEGS ) );
	}

	private List<RobotEntry> createMockRobotData() {
		// Creating and recording part mocks
		Part head1Mock = createMock( Part.class );
		Part torso1Mock = createMock( Part.class );
		Part armLeft1Mock = createMock( Part.class );
		Part armRight1Mock = createMock( Part.class );
		Part legs1Mock = createMock( Part.class );
		
		Part head2Mock = createMock( Part.class );
		Part torso2Mock = createMock( Part.class );
		Part armLeft2Mock = createMock( Part.class );
		Part armRight2Mock = createMock( Part.class );
		Part legs2Mock = createMock( Part.class );
		
		Part[] mocks = new Part[]{ head1Mock, torso1Mock, armLeft1Mock, armRight1Mock, legs1Mock,
				head2Mock, torso2Mock, armLeft2Mock, armRight2Mock, legs2Mock };
				
		reset( head1Mock, torso1Mock, armLeft1Mock, armRight1Mock, legs1Mock,
				head2Mock, torso2Mock, armLeft2Mock, armRight2Mock, legs2Mock);
		
		head1Mock.getName(); expectLastCall().andReturn( "Head1" ).once();
		torso1Mock.getName(); expectLastCall().andReturn( "Torso1" ).once();
		armLeft1Mock.getName(); expectLastCall().andReturn( "ArmLeft1" ).once();
		armRight1Mock.getName(); expectLastCall().andReturn( "ArmRight1" ).once();
		legs1Mock.getName(); expectLastCall().andReturn( "Legs1" ).once();
		
		head2Mock.getName(); expectLastCall().andReturn( "Head2" ).once();
		torso2Mock.getName(); expectLastCall().andReturn( "Torso2" ).once();
		armLeft2Mock.getName(); expectLastCall().andReturn( "ArmLeft2" ).once();
		armRight2Mock.getName(); expectLastCall().andReturn( "ArmRight2" ).once();
		legs2Mock.getName(); expectLastCall().andReturn( "Legs2" ).once();
		
		for( Part mockPart : mocks ) {			
			mockPart.getAttribute( Part.ENERGY ); expectLastCall().andReturn( "0" ).once();
			mockPart.getAttribute( Part.ATTACK ); expectLastCall().andReturn( "0" ).once();
			mockPart.getAttribute( Part.DEFENSE ); expectLastCall().andReturn( "0" ).once();
			mockPart.getAttribute( Part.SPEED ); expectLastCall().andReturn( "0" ).once();		
			mockPart.getPrice(); expectLastCall().andReturn( 0 ).once();	
			mockPart.getActions(); expectLastCall().andReturn( new LinkedList<String>() ).once();
		}
		
		replay( head1Mock, torso1Mock, armLeft1Mock, armRight1Mock, legs1Mock,
				head2Mock, torso2Mock, armLeft2Mock, armRight2Mock, legs2Mock);
		
		// Creating part entries
		PartEntry head1 = new PartEntry( head1Mock, null );
		PartEntry torso1 = new PartEntry( torso1Mock, null );
		PartEntry armLeft1 = new PartEntry( armLeft1Mock, null );
		PartEntry armRight1 = new PartEntry( armRight1Mock, null );
		PartEntry legs1 = new PartEntry( legs1Mock, null );
		
		PartEntry head2 = new PartEntry( head2Mock, null );
		PartEntry torso2 = new PartEntry( torso2Mock, null );
		PartEntry armLeft2 = new PartEntry( armLeft2Mock, null );
		PartEntry armRight2 = new PartEntry( armRight2Mock, null );
		PartEntry legs2 = new PartEntry( legs2Mock, null );
		
		// Creating part mappings
		Map<RobotPartCategory, PartEntry> partMapping1 = new HashMap<RobotPartCategory, PartEntry>();
		
		partMapping1.put( RobotPartCategory.HEAD, head1 );
		partMapping1.put( RobotPartCategory.TORSO, torso1 );
		partMapping1.put( RobotPartCategory.ARM_LEFT, armLeft1 );
		partMapping1.put( RobotPartCategory.ARM_RIGHT, armRight1 );
		partMapping1.put( RobotPartCategory.LEGS, legs1 );
		
		Map<RobotPartCategory, PartEntry> partMapping2 = new HashMap<RobotPartCategory, PartEntry>();
		
		partMapping2.put( RobotPartCategory.HEAD, head2 );
		partMapping2.put( RobotPartCategory.TORSO, torso2 );
		partMapping2.put( RobotPartCategory.ARM_LEFT, armLeft2 );
		partMapping2.put( RobotPartCategory.ARM_RIGHT, armRight2);
		partMapping2.put( RobotPartCategory.LEGS, legs2 );
		
		// Creating robot entries
		RobotEntry robot1 = new RobotEntry( TEST_ROBOT_OWNER, TEST_ROBOT_1, partMapping1, 0, 0 );
		RobotEntry robot2 = new RobotEntry( TEST_ROBOT_OWNER, TEST_ROBOT_2, partMapping2, 0, 0 );
		
		// Checking if robots were correctly set
		assertEquals( TEST_ROBOT_1, robot1.getRobotName() );
		assertEquals( TEST_ROBOT_OWNER, robot1.getOwnerName() );
		
		assertEquals( "Head1", robot1.getPart( RobotPartCategory.HEAD ).getName() );
		assertEquals( "Torso1", robot1.getPart( RobotPartCategory.TORSO ).getName() );
		assertEquals( "ArmLeft1", robot1.getPart( RobotPartCategory.ARM_LEFT ).getName() );
		assertEquals( "ArmRight1", robot1.getPart( RobotPartCategory.ARM_RIGHT ).getName() );
		assertEquals( "Legs1", robot1.getPart( RobotPartCategory.LEGS ).getName() );
		
		assertEquals( TEST_ROBOT_2, robot2.getRobotName() );
		assertEquals( TEST_ROBOT_OWNER, robot2.getOwnerName() );
		
		assertEquals( "Head2", robot2.getPart( RobotPartCategory.HEAD ).getName() );
		assertEquals( "Torso2", robot2.getPart( RobotPartCategory.TORSO ).getName() );
		assertEquals( "ArmLeft2", robot2.getPart( RobotPartCategory.ARM_LEFT ).getName() );
		assertEquals( "ArmRight2", robot2.getPart( RobotPartCategory.ARM_RIGHT ).getName() );
		assertEquals( "Legs2", robot2.getPart( RobotPartCategory.LEGS ).getName() );
		
		// Creating robots list
		List<RobotEntry> robots = new LinkedList<RobotEntry>();
		robots.add( robot1 );
		robots.add( robot2 );
		return robots;
	}
	
	public void testParseGameBoard() {
		
		Map<String, Map<String, RobotPosition>> boardPositions = new TreeMap<String, Map<String,RobotPosition>>();
		
		// Creating player 1 robot mapping
		Map<String, RobotPosition> player1 = new TreeMap<String, RobotPosition>();
		player1.put( TEST_ROBOT_1, new RobotPosition( FacingSides.EAST, 1, 2 ) );
		player1.put( TEST_ROBOT_2, new RobotPosition( FacingSides.NORTH, 3, 4 ) );
		
		// Creating player 2 robot mapping
		Map<String, RobotPosition> player2 = new TreeMap<String, RobotPosition>();
		player2.put( TEST_ROBOT_2, new RobotPosition( FacingSides.SOUTH, 5, 6 ) );
		
		boardPositions.put( ALIAS1, player1 );
		boardPositions.put( ALIAS2, player2 );
		
		// Creating message
		Message message = ProtocolMessageFactory.buildGameBoardMessage( boardPositions );
		
		// Parsing board
		Map<String, Map<String, RobotPosition>> parsedPositions = ProtocolMessageParser.parseGameBoard( message );
		
		assertTrue( parsedPositions.containsKey( ALIAS1 ) );
		assertTrue( parsedPositions.containsKey( ALIAS2 ) );
		
		assertTrue( parsedPositions.get( ALIAS1 ).containsKey( TEST_ROBOT_1 ) );
		assertTrue( parsedPositions.get( ALIAS1 ).containsKey( TEST_ROBOT_2 ) );
		
		assertTrue( parsedPositions.get( ALIAS2 ).containsKey( TEST_ROBOT_2 ) );
		
		assertEquals( 1, parsedPositions.get( ALIAS1 ).get( TEST_ROBOT_1 ).getX() );
		assertEquals( 2, parsedPositions.get( ALIAS1 ).get( TEST_ROBOT_1 ).getY() );
		assertEquals( FacingSides.EAST, parsedPositions.get( ALIAS1 ).get( TEST_ROBOT_1 ).getFacing() );
		
		assertEquals( 3, parsedPositions.get( ALIAS1 ).get( TEST_ROBOT_2 ).getX() );
		assertEquals( 4, parsedPositions.get( ALIAS1 ).get( TEST_ROBOT_2 ).getY() );
		assertEquals( FacingSides.NORTH, parsedPositions.get( ALIAS1 ).get( TEST_ROBOT_2 ).getFacing() );
		
		assertEquals( 5, parsedPositions.get( ALIAS2 ).get( TEST_ROBOT_2 ).getX() );
		assertEquals( 6, parsedPositions.get( ALIAS2 ).get( TEST_ROBOT_2 ).getY() );
		assertEquals( FacingSides.SOUTH, parsedPositions.get( ALIAS2 ).get( TEST_ROBOT_2 ).getFacing() );
	}
}
